/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include <string.h>
#include <stdlib.h>

#include "ccitt.h"


typedef struct CODELINE
{
    short runLength;
    short bitLength;
    short code;

} CODELINE;

typedef struct CONTROLCODE
{
    short bitLength;
    short code;

} CONTROLCODE;


const int EOL = 1; // EOL code value - 0000 0000 0000 0001
const int EOFB = 1; // EOFB code value - 0000 0000 0000 0001 0000 0001

// white terminating codes
CODELINE wtCodes[] = {
    { 0, 8, 0x35 },	// 0011 0101
    { 1, 6, 0x7 },	// 0001 11
    { 2, 4, 0x7 },	// 0111
    { 3, 4, 0x8 },	// 1000
    { 4, 4, 0xB },	// 1011
    { 5, 4, 0xC },	// 1100
    { 6, 4, 0xE },	// 1110
    { 7, 4, 0xF },	// 1111
    { 8, 5, 0x13 },	// 1001 1
    { 9, 5, 0x14 },	// 1010 0
    { 10, 5, 0x7 },	// 0011 1
    { 11, 5, 0x8 },	// 0100 0
    { 12, 6, 0x8 },	// 0010 00
    { 13, 6, 0x3 },	// 0000 11
    { 14, 6, 0x34 },	// 1101 00
    { 15, 6, 0x35 },	// 1101 01
    { 16, 6, 0x2A },	// 1010 10
    { 17, 6, 0x2B },	// 1010 11
    { 18, 7, 0x27 },    // 0100 111
    { 19, 7, 0xC },	// 0001 100
    { 20, 7, 0x8 },	// 0001 000
    { 21, 7, 0x17 },    // 0010 111
    { 22, 7, 0x3 },	// 0000 011
    { 23, 7, 0x4 },	// 0000 100
    { 24, 7, 0x28 },    // 0101 000
    { 25, 7, 0x2B },    // 0101 011
    { 26, 7, 0x13 },    // 0010 011
    { 27, 7, 0x24 },    // 0100 100
    { 28, 7, 0x18 },    // 0011 000
    { 29, 8, 0x2 },	// 0000 0010
    { 30, 8, 0x3 },	// 0000 0011
    { 31, 8, 0x1A },    // 0001 1010
    { 32, 8, 0x1B },    // 0001 1011
    { 33, 8, 0x12 },    // 0001 0010
    { 34, 8, 0x13 },    // 0001 0011
    { 35, 8, 0x14 },    // 0001 0100
    { 36, 8, 0x15 },    // 0001 0101
    { 37, 8, 0x16 },    // 0001 0110
    { 38, 8, 0x17 },    // 0001 0111
    { 39, 8, 0x28 },    // 0010 1000
    { 40, 8, 0x29 },    // 0010 1001
    { 41, 8, 0x2A },    // 0010 1010
    { 42, 8, 0x2B },    // 0010 1011
    { 43, 8, 0x2C },    // 0010 1100
    { 44, 8, 0x2D },    // 0010 1101
    { 45, 8, 0x4 },	// 0000 0100
    { 46, 8, 0x5 },	// 0000 0101
    { 47, 8, 0xA },	// 0000 1010
    { 48, 8, 0xB },	// 0000 1011
    { 49, 8, 0x52 },    // 0101 0010
    { 50, 8, 0x53 },    // 0101 0011
    { 51, 8, 0x54 },    // 0101 0100
    { 52, 8, 0x55 },    // 0101 0101
    { 53, 8, 0x24 },    // 0010 0100
    { 54, 8, 0x25 },    // 0010 0101
    { 55, 8, 0x58 },    // 0101 1000
    { 56, 8, 0x59 },    // 0101 1001
    { 57, 8, 0x5A },    // 0101 1010
    { 58, 8, 0x5B },    // 0101 1011
    { 59, 8, 0x4A },    // 0100 1010
    { 60, 8, 0x4B },    // 0100 1011
    { 61, 8, 0x32 },    // 0011 0010
    { 62, 8, 0x33 },    // 0011 0011
    { 63, 8, 0x34 }	// 0011 0100
};

// white make-up codes
CODELINE wmCodes[] = {
    { 64, 5, 0x1B },    // 1101 1
    { 128, 5, 0x12 },   // 1001 0
    { 192, 6, 0x17 },   // 0101 11
    { 256, 7, 0x37 },   // 0110 111
    { 320, 8, 0x36 },   // 0011 0110
    { 384, 8, 0x37 },   // 0011 0111
    { 448, 8, 0x64 },   // 0110 0100
    { 512, 8, 0x65 },   // 0110 0101
    { 576, 8, 0x68 },   // 0110 1000
    { 640, 8, 0x67 },   // 0110 0111
    { 704, 9, 0xCC },   // 0110 0110 0
    { 768, 9, 0xCD },   // 0110 0110 1
    { 832, 9, 0xD2 },   // 0110 1001 0
    { 896, 9, 0xD3 },   // 0110 1001 1
    { 960, 9, 0xD4 },   // 0110 1010 0
    { 1024, 9, 0xD5 },  // 0110 1010 1
    { 1088, 9, 0xD6 },  // 0110 1011 0
    { 1152, 9, 0xD7 },  // 0110 1011 1
    { 1216, 9, 0xD8 },  // 0110 1100 0
    { 1280, 9, 0xD9 },  // 0110 1100 1
    { 1344, 9, 0xDA },  // 0110 1101 0
    { 1408, 9, 0xDB },  // 0110 1101 1
    { 1472, 9, 0x98 },  // 0100 1100 0
    { 1536, 9, 0x99 },  // 0100 1100 1
    { 1600, 9, 0x9A },  // 0100 1101 0
    { 1664, 6, 0x18 },  // 0110 00
    { 1728, 9, 0x9B },  // 0100 1101 1
    // common for both colors
    { 1792, 11, 0x8 },  // 0000 0001 000
    { 1856, 11, 0xC },  // 0000 0001 100
    { 1920, 11, 0xD },  // 0000 0001 101
    { 1984, 12, 0x12 }, // 0000 0001 0010
    { 2048, 12, 0x13 }, // 0000 0001 0011
    { 2112, 12, 0x14 }, // 0000 0001 0100
    { 2176, 12, 0x15 }, // 0000 0001 0101
    { 2240, 12, 0x16 }, // 0000 0001 0110
    { 2304, 12, 0x17 }, // 0000 0001 0111
    { 2368, 12, 0x1C }, // 0000 0001 1100
    { 2432, 12, 0x1D }, // 0000 0001 1101
    { 2496, 12, 0x1E }, // 0000 0001 1110
    { 2560, 12, 0x1F }, // 0000 0001 1111
};

// black terminating codes
CODELINE btCodes[] = {
    { 0, 10, 0x37 },    // 0000 1101 11
    { 1, 3, 0x2 },	// 010
    { 2, 2, 0x3 },	// 11
    { 3, 2, 0x2 },	// 10
    { 4, 3, 0x3 },	// 011
    { 5, 4, 0x3 },	// 0011
    { 6, 4, 0x2 },	// 0010
    { 7, 5, 0x3 },	// 0001 1
    { 8, 6, 0x5 },	// 0001 01
    { 9, 6, 0x4 },	// 0001 00
    { 10, 7, 0x4 },	// 0000 100
    { 11, 7, 0x5 },	// 0000 101
    { 12, 7, 0x7 },	// 0000 111
    { 13, 8, 0x4 },	// 0000 0100
    { 14, 8, 0x7 },	// 0000 0111
    { 15, 9, 0x18 },    // 0000 1100 0
    { 16, 10, 0x17 },   // 0000 0101 11
    { 17, 10, 0x18 },   // 0000 0110 00
    { 18, 10, 0x8 },    // 0000 0010 00
    { 19, 11, 0x67 },   // 0000 1100 111
    { 20, 11, 0x68 },   // 0000 1101 000
    { 21, 11, 0x6C },   // 0000 1101 100
    { 22, 11, 0x37 },   // 0000 0110 111
    { 23, 11, 0x28 },   // 0000 0101 000
    { 24, 11, 0x17 },   // 0000 0010 111
    { 25, 11, 0x18 },   // 0000 0011 000
    { 26, 12, 0xCA },   // 0000 1100 1010
    { 27, 12, 0xCB },   // 0000 1100 1011
    { 28, 12, 0xCC },   // 0000 1100 1100
    { 29, 12, 0xCD },   // 0000 1100 1101
    { 30, 12, 0x68 },   // 0000 0110 1000
    { 31, 12, 0x69 },   // 0000 0110 1001
    { 32, 12, 0x6A },   // 0000 0110 1010
    { 33, 12, 0x6B },   // 0000 0110 1011
    { 34, 12, 0xD2 },   // 0000 1101 0010
    { 35, 12, 0xD3 },   // 0000 1101 0011
    { 36, 12, 0xD4 },   // 0000 1101 0100
    { 37, 12, 0xD5 },   // 0000 1101 0101
    { 38, 12, 0xD6 },   // 0000 1101 0110
    { 39, 12, 0xD7 },   // 0000 1101 0111
    { 40, 12, 0x6C },   // 0000 0110 1100
    { 41, 12, 0x6D },   // 0000 0110 1101
    { 42, 12, 0xDA },   // 0000 1101 1010
    { 43, 12, 0xDB },   // 0000 1101 1011
    { 44, 12, 0x54 },   // 0000 0101 0100
    { 45, 12, 0x55 },   // 0000 0101 0101
    { 46, 12, 0x56 },   // 0000 0101 0110
    { 47, 12, 0x57 },   // 0000 0101 0111
    { 48, 12, 0x64 },   // 0000 0110 0100
    { 49, 12, 0x65 },   // 0000 0110 0101
    { 50, 12, 0x52 },   // 0000 0101 0010
    { 51, 12, 0x53 },   // 0000 0101 0011
    { 52, 12, 0x24 },   // 0000 0010 0100
    { 53, 12, 0x37 },   // 0000 0011 0111
    { 54, 12, 0x38 },   // 0000 0011 1000
    { 55, 12, 0x27 },   // 0000 0010 0111
    { 56, 12, 0x28 },   // 0000 0010 1000
    { 57, 12, 0x58 },   // 0000 0101 1000
    { 58, 12, 0x59 },   // 0000 0101 1001
    { 59, 12, 0x2B },   // 0000 0010 1011
    { 60, 12, 0x2C },   // 0000 0010 1100
    { 61, 12, 0x5A },   // 0000 0101 1010
    { 62, 12, 0x66 },   // 0000 0110 0110
    { 63, 12, 0x67 }	// 0000 0110 0111
};

// black make-up codes
CODELINE bmCodes[] = {
    { 64, 10, 0xF },    // 0000 0011 11
    { 128, 12, 0xC8 },  // 0000 1100 1000
    { 192, 12, 0xC9 },  // 0000 1100 1001
    { 256, 12, 0x5B },  // 0000 0101 1011
    { 320, 12, 0x33 },  // 0000 0011 0011
    { 384, 12, 0x34 },  // 0000 0011 0100
    { 448, 12, 0x35 },  // 0000 0011 0101
    { 512, 13, 0x6C },  // 0000 0011 0110 0
    { 576, 13, 0x6D },  // 0000 0011 0110 1
    { 640, 13, 0x4A },  // 0000 0010 0101 0
    { 704, 13, 0x4B },  // 0000 0010 0101 1
    { 768, 13, 0x4C },  // 0000 0010 0110 0
    { 832, 13, 0x4D },  // 0000 0010 0110 1
    { 896, 13, 0x72 },  // 0000 0011 1001 0
    { 960, 13, 0x73 },  // 0000 0011 1001 1
    { 1024, 13, 0x74 }, // 0000 0011 1010 0
    { 1088, 13, 0x75 }, // 0000 0011 1010 1
    { 1152, 13, 0x76 }, // 0000 0011 1011 0
    { 1216, 13, 0x77 }, // 0000 0011 1011 1
    { 1280, 13, 0x52 }, // 0000 0010 1001 0
    { 1344, 13, 0x53 }, // 0000 0010 1001 1
    { 1408, 13, 0x54 }, // 0000 0010 1010 0
    { 1472, 13, 0x55 }, // 0000 0010 1010 1
    { 1536, 13, 0x5A }, // 0000 0010 1101 0
    { 1600, 13, 0x5B }, // 0000 0010 1101 1
    { 1664, 13, 0x64 }, // 0000 0011 0010 0
    { 1728, 13, 0x65 }, // 0000 0011 0010 1
    // common for both colors
    { 1792, 11, 0x8 },  // 0000 0001 000
    { 1856, 11, 0xC },  // 0000 0001 100
    { 1920, 11, 0xD },  // 0000 0001 101
    { 1984, 12, 0x12 }, // 0000 0001 0010
    { 2048, 12, 0x13 }, // 0000 0001 0011
    { 2112, 12, 0x14 }, // 0000 0001 0100
    { 2176, 12, 0x15 }, // 0000 0001 0101
    { 2240, 12, 0x16 }, // 0000 0001 0110
    { 2304, 12, 0x17 }, // 0000 0001 0111
    { 2368, 12, 0x1C }, // 0000 0001 1100
    { 2432, 12, 0x1D }, // 0000 0001 1101
    { 2496, 12, 0x1E }, // 0000 0001 1110
    { 2560, 12, 0x1F }	// 0000 0001 1111
};

// black make-up codes
CONTROLCODE verticalCodes[] = {
    // a1 to the left of b1
    { 7, 0x02 },		// VL(3)
    { 6, 0x02 },		// VL(2)
    { 3, 0x02 },		// VL(1)
    // a1 just under b1
    { 1, 0x01 },		// V(0)
    // a1 to the right of b1
    { 3, 0x03 },		// VR(1)
    { 6, 0x03 },		// VR(2)
    { 7, 0x03 }			// VR(3)
};

CONTROLCODE* verticalCodePointer = verticalCodes + 3;

// black make-up codes
CODELINE generalCodes[] = {
    { 0, 24, 0x1 }		// EOFB - end of facsimile block
};


// finds to specified bit in the line
byte_t CcittEncoder::getBit( byte_t* bytes, int index )
{
    if ( index < 0 ) return 0;

    int byteIndex = index >> 3;
    byte_t* base = bytes + byteIndex;
    byte_t baseChar = *base;
    byte_t bitIndex = index & 7;
    byte_t bit = ( baseChar >> ( 7 - bitIndex ) ) & 0x01;
    return m_invertBits ? !bit : bit;
}

// returns the index of the next chaging element (differing from the one identified by 'bit' parameter) to the right of 'startIndex' element
int CcittEncoder::findChangingElement( byte_t* line, int startIndex, int width, byte_t bit, byte_t& value )
{
    if ( startIndex == -1 )
    {
        bit = 0;
    }
    else if ( startIndex >= width )
    {
        value = 0;
        return width;
    }

    int i = startIndex + 1;
    while ( i < width )
    {
        value = getBit( line, i );

        if ( value != bit )
        {
            return i;
        }

        i++;
    }

    value = 0;
    return i;
}

// writes number of bits to the output buffer and updates the buffer pointer
void CcittEncoder::writeByte( byte_t* buffer, byte_t bits, int number )
{
    int byteIndex = m_bp >> 3;
    int offset =  m_bp - ( ( m_bp >> 3 ) << 3 ); //8 - ( ( ( ( bp + 7 ) & ( ~7 ) ) ) - bp );
    if ( offset == 0 )
    {
        buffer[ byteIndex ] = bits << ( 8 - number );
    }
    else
    {
        if ( offset + number <= 8 )
        {
            buffer[ byteIndex ] |= bits << ( 8 - offset - number );
        }
        else
        {
            buffer[ byteIndex ] |= bits >> ( offset - ( 8 - number ) );
            buffer[ byteIndex + 1 ] = ( byte_t ) ( bits << ( 16 - ( offset + number ) ) );
        }
    }

    m_bp += number;
}

void CcittEncoder::writeShort( byte_t* buffer, unsigned short bits, int number )
{
    if ( number <= 8 )
    {
        writeByte( buffer, ( byte_t ) bits, number );
    }
    else
    {
        writeByte( buffer, ( byte_t ) ( bits >> 8 ), number - 8 );
        writeByte( buffer, ( byte_t ) ( bits & 0x00FF ), 8 );
    }
}

void CcittEncoder::writeRunLength( byte_t* buffer, unsigned short length, byte_t bit )
{
    CODELINE codeLine = { 0 };

    while ( length > 2623 )
    {
        // make-up code
        codeLine = ( bit == 0 ? wmCodes : bmCodes )[ 39 ]; // 2560 / 64
        writeShort( buffer, codeLine.code, codeLine.bitLength );

        length -= codeLine.runLength;
    }

    if ( length > 63 )
    {
        // make-up part
        codeLine = ( bit == 0 ? wmCodes : bmCodes )[ ( length >> 6 ) - 1 ]; // makeup table index
        writeShort( buffer, codeLine.code, codeLine.bitLength );

        // terminating part
        length -= codeLine.runLength;
    }

    codeLine = ( bit == 0 ? wtCodes : btCodes )[ length ];
    writeShort( buffer, codeLine.code, codeLine.bitLength );
}

// source is byte-aligned
// @bitsInverted - specifies whether the image colors are inverted, i.e. 1 - white, 0 - black (normally should be 0 - white, 1 - black)
int CcittEncoder::encodeG4( byte_t* dest, byte_t* source, int width, int height, byte_t alignment, char direction, bool bitsInverted )
{
    if ( width <= 0 || height <= 0 ) return 0;

    //size_t outputSize = 0;
    m_bp = 0;
    m_invertBits = bitsInverted;

    // each bit line is byte aligned
    //size_t lineByteLength = ( ( ( width + 7 ) & ( ~7 ) ) >> 3 );
    size_t lineByteLength = ( ( ( width + ( --alignment ) ) & ( ~alignment ) ) >> 3 );

    // initial reference line
    byte_t* whiteLine = new byte_t[ lineByteLength ];
    ::memset( whiteLine, m_invertBits ? 0xFF : 0, lineByteLength );
    byte_t* referenceLine = whiteLine;

    // the coding line
    byte_t* codingLine = direction > 0 ? source : ( source + ( ( height - 1 ) * lineByteLength ) );

    // changing picture element values
    byte_t a0v = 0, a1v = 0, a2v = 0, b1v = 0, b2v = 0;

    // changing picture element indexes
    int a0 = -1, a1 = 0, b1 = 0, a2 = 0, b2 = 0;

    bool lineBeginning = true; // identifies whether it is the beginning of the line

    for ( int line = 1; ; )
    {
        a1 = findChangingElement( codingLine, a0, width, a0v, a1v );
        if ( lineBeginning )
        {
            b1 = findChangingElement( referenceLine, a0, width, a0v, b1v );
        }
        else
        {
            b1 = findChangingElement( referenceLine, a0, width, getBit( referenceLine, a0 ), b1v );
            if ( b1v == a0v )
            {
                b1 = findChangingElement( referenceLine, b1, width, b1v, b1v );
            }
        }
        b2 = findChangingElement( referenceLine, b1, width, b1v, b2v );

        if ( b2 < a1 )
        {
            // pass mode coding
            writeByte( dest, 0x01U, 4 ); // 0001 (P)

            // put a0 just under b2
            a0 = b2;
        }
        else
        {
            int a1b1 = a1 - b1;
            if ( a1b1 <= 3 && a1b1 >= -3 ) // | a1 b1 | <= 3
            {
                // vertical mode coding
                CONTROLCODE code = *( verticalCodePointer + a1b1 );

                writeByte( dest, ( byte_t ) code.code, ( int ) code.bitLength );

                // put a0 on a1
                a0 = a1;
                a0v = a1v;
            }
            else
            {
                // detect a2
                a2 = findChangingElement( codingLine, a1, width, a1v, a2v );

                // horizontal mode coding
                writeByte( dest, 0x01U, 3 ); // 001 (H)

                int a0a1 = a1 - ( lineBeginning ? a0 + 1 : a0 );
                int a1a2 = a2 - a1;

                unsigned short runLength1 = 0, runLength2 = 0;
                byte_t runBit1 = 0;

                if ( lineBeginning && a0v != 0 )
                {
                    runLength1 = 0;
                    runBit1 = 0;
                    runLength2 = a0a1;
                }
                else
                {
                    runLength1 = a0a1;
                    runLength2 = a1a2;
                    runBit1 = a0v;
                }

                writeRunLength( dest, runLength1, runBit1 );
                writeRunLength( dest, runLength2, !runBit1 );

                // put a0 on a2
                a0 = a2;
            }
        }

        if ( a0 >= width ) // end of line /*findChangingElement( codingLine, a0, width, a0v, a1v )*/
        {
            if ( line == height ) // end of facsimile block
            {
                // end-of-facsimile block (EOFB)
                writeShort( dest, 0x0010, 16 );
                writeByte( dest, 0x01, 8 );

                // TODO: pad bits

                delete whiteLine;

                return ( ( ( m_bp + 7 ) & ( ~7 ) ) >> 3 );
            }
            else
            {
                referenceLine = codingLine;
                codingLine += direction > 0 ? ( int ) lineByteLength : - ( ( int ) lineByteLength );
                a0 = -1;
                a0v = 0;

                line++;
                lineBeginning = true;
            }
        }
        else
        {
            lineBeginning = false;
        }
    }

    return 0;
}


